<!--
* @Description: 二维码
* @Version: 1.0.0
* @Author: Leo https://github.com/lfcleo
* @DateRange: 2024-05-25 11:08:02
* @LastEditTime: 2024-05-25 11:08:02
-->
<template>
    <div :style="QRCodeStyle">
        <canvas :style="QRCodeStyle" :id="canvasId"></canvas>
    </div>
</template>

<script setup lang="ts">
import {ref, onMounted, computed} from "vue";
import QRCode, {type QRCodeRenderersOptions} from "qrcode";
import {generateUUID} from "@/utils/uuid";

interface QRCodeProps {
    size?: number; // 二维码大小 ==> 默认200
    iconSize?: number; // 二维码图标大小 ==> 默认40
    content: string; // 二维码内容 ==> 必填
    logo?: string; // 二维码logo ==> 默认无
    options?: QRCodeRenderersOptions; // 二维码配置 ==> 默认无
    errorLevel?: "L" | "M" | "Q" | "H"; // 二维码容错级别 ==> 默认H
}

// 接收父组件参数并设置默认值
const props = withDefaults(defineProps<QRCodeProps>(), {
    size: 200,
    iconSize: 40,
    errorLevel: "H"
});

const qrCodeUrl = ref<string>("");
const canvasId = ref("canvas" + generateUUID());    //防止页面有多个二维码，导致canvasId一致的问题

const QRCodeStyle = computed(() => {
    return {
        width: props.size + "px",
        height: props.size + "px"
    };
});

// 生成二维码
const initQRCode = async () => {
    // 1. 生成 QR 码（调整配色和边距）
    const canvasRef: any = await QRCode.toCanvas(
        document.getElementById(canvasId.value),
        props.content,
        {
            width: props.size,
            margin: 4, // 增大边距，为外边框留空间
            errorCorrectionLevel: props.errorLevel, // 建议用 'H' 级（最高容错），避免 Logo 遮挡信息
            color: {
                dark: "#333333", // 深灰色（非纯黑，更柔和）
                light: "#ffffff" // 白色背景
            },
            ...props.options
        }
    );

    const ctx = canvasRef.getContext("2d");
    const qrSize = canvasRef.width;

    // 2. 绘制 QR 码外边框（若目标样式有边框）
    const borderWidth = 2; // 边框粗细
    ctx.strokeStyle = "#f0f0f0"; // 浅灰色边框
    ctx.lineWidth = borderWidth;
    ctx.strokeRect(
        borderWidth / 2,
        borderWidth / 2,
        qrSize - borderWidth,
        qrSize - borderWidth
    );

    if (props.logo) {
        // 3. Logo 双层背景（内层白色 + 外层浅色边框）
        const iconSize = props.iconSize;
        const outerBgPadding = 6; // 外层背景比 Logo 大的尺寸
        const innerBgPadding = 2; // 内层白色背景比 Logo 大的尺寸
        const borderRadius = 12; // 圆角半径（根据目标样式调整）

        // 外层背景（浅灰色边框效果）
        const outerBgSize = iconSize + outerBgPadding * 2;
        const outerBgX = (qrSize - outerBgSize) / 2;
        const outerBgY = (qrSize - outerBgSize) / 2;
        ctx.fillStyle = "#f5f5f5"; // 浅灰色外层
        ctx.beginPath();
        ctx.roundRect(outerBgX, outerBgY, outerBgSize, outerBgSize, borderRadius);
        ctx.fill();

        // 内层背景（白色，覆盖外层中间部分）
        const innerBgSize = iconSize + innerBgPadding * 2;
        const innerBgX = (qrSize - innerBgSize) / 2;
        const innerBgY = (qrSize - innerBgSize) / 2;
        ctx.fillStyle = "#ffffff"; // 白色内层
        ctx.beginPath();
        ctx.roundRect(innerBgX, innerBgY, innerBgSize, innerBgSize, borderRadius - 2); // 内层圆角稍小
        ctx.fill();

        // 4. 绘制 Logo 并添加圆角（若 Logo 本身需要圆角）
        const iconX = (qrSize - iconSize) / 2;
        const iconY = (qrSize - iconSize) / 2;
        const image = new Image();
        image.crossOrigin = "Anonymous";
        image.onload = () => {
            // 给 Logo 加圆角（避免直角突兀）
            ctx.save();
            ctx.beginPath();
            ctx.roundRect(iconX, iconY, iconSize, iconSize, 6); // Logo 自身圆角
            ctx.clip(); // 裁剪出圆角区域
            ctx.drawImage(image, iconX, iconY, iconSize, iconSize);
            ctx.restore();

            // 最终生成图片 URL
            qrCodeUrl.value = canvasRef.toDataURL();
        };
        image.src = props.logo;
    } else {
        qrCodeUrl.value = canvasRef.toDataURL();
    }
};

// 下载二维码
const downLoadQRCode = (fileName: string = generateUUID(), fileType: string = ".png") => {
    const exportFile = document.createElement("a");
    exportFile.style.display = "none";
    exportFile.download = `${fileName}${fileType}`;
    exportFile.href = qrCodeUrl.value;
    document.body.appendChild(exportFile);
    exportFile.click();
    // 去除下载对 url 的影响
    document.body.removeChild(exportFile);
};

onMounted(() => {
    initQRCode();
});

defineExpose({
    downLoadQRCode
});
</script>